#include <stdlib.h>
#include <strings.h>
#include  <netinet/in.h>

#include "type_announce.h"
#include "type_routingtable.h"

int GenerateTypeAnnouncePacket(char* buf)
{
    char * tail = NULL;                                     // Tail of the packet

    // Fill the packet header
    ta_header_t * packet_header = (ta_header_t*) buf;       // Packet first contains ta_header_t
    tail = buf + sizeof(ta_header_t);

    // Populate header fields
    packet_header->seq_no = htonl(seqno); 
    seqno++; 
    packet_header->multicast_group_count = 0;               // Initialize to 0, will change at the end

    tail = buf + sizeof(ta_header_t);                       // Move tail

    printf("  Generating TA packet from %s, seq_no = %d.\n", inet_ntoa(local_ip_addr), seqno);

    // Fill in the rest of the packet now with the group and neighbor information.
    ta_group_entry_t * multicast_group_entry = NULL;        // Entry of multicast group to fill 
    ta_neighbor_entry_t * neighbor_entry = NULL;            // Entry of neighbors information

    // Iteratively add the groups into the buffer.
    multicast_group_node* mglIter = NULL;
    for (mglIter = GetRoutingTable();
        mglIter != NULL;
        mglIter = mglIter->next)
    {
        multicast_group_entry = (ta_group_entry_t*) tail;     // Add this group to the packet buffer

        // Populate the group information
        multicast_group_entry->multicast_group_addr = htonl(mglIter->multicast_group_addr); 
        multicast_group_entry->neighbors_count = 0;

        tail += sizeof(ta_group_entry_t);                     // Move tail

        printf("    Multicast group: grp_addr = %s.\n", inet_ntoa(mglIter->multicast_group_addr));

        // Now iteratively add the neighbors that belong in this group.
        multicast_neighbor_node * nbrIter = NULL;
        for (nbrIter = mglIter->neighborHead;
            nbrIter != NULL;
            nbrIter = nbrIter->next)
        {
            neighbor_entry = (ta_neighbor_entry_t*) tail;

            // Populate the neighbor information
            neighbor_entry->addr = htonl(nbrIter->multicast_neighbor_addr);
            copy_bloom_filter(neighbor_entry->ABF, nbrIter->ABF); 

            multicast_group_entry->neighbors_count++; 
            tail += sizeof(ta_neighbor_entry_t);               // Move tail

            char hexABF[BLOOM_FILTER_SIZE * 2 + 1];
            bloom_filter_to_hex_string(nbrIter->ABF, hexABF, BLOOM_FILTER_SIZE);
            printf("      Neighbor: neighbor_addr = %s, ABF = %s.\n", inet_ntoa(neighbor_entry->addr), hexABF);
        }

        packet_header->multicast_group_count++; 
    }
    return tail - buf; 
}

void ProcessTypeAnnouncePacket(char* buf, u_int32_t srcAddr)
{
    ta_header_t* packet_header = (ta_header_t*) buf;
    long seq_no = ntohl(packet_header->seq_no); 
    char * tail = buf + sizeof(ta_header_t);
    u_int32_t grp_addr; 
    u_int32_t neighbor_addr; 

    printf("  Processing TA packet from %s, seq_no = %d.\n", inet_ntoa(srcAddr), seq_no);
    printf("    Local IP address = %s.\n", inet_ntoa(local_ip_addr));

    // Traverse the packet 
    int i = 0;
    int j; 
    for (i = 0; i < packet_header->multicast_group_count; i++)
    {
        // Iterate through the contents of the packet.  One multicast group at a time:
        ta_group_entry_t * currentGroup = (ta_group_entry_t*) tail;
        tail += sizeof(ta_group_entry_t);       // Now tail points to the first neighbor of this group.
        grp_addr = ntohl(currentGroup->multicast_group_addr);

        printf("    Multicast group %d: grp_addr = %s.\n", i, inet_ntoa(grp_addr));

        for (j = 0; j < currentGroup->neighbors_count; j++)
        {
            ta_neighbor_entry_t * currentNeighbor = (ta_neighbor_entry_t*) tail;
            tail += sizeof(ta_neighbor_entry_t);
            neighbor_addr = ntohl(currentNeighbor->addr);

            printf("      Neighbor %d: neighbor_addr = %s.\n", j, inet_ntoa(neighbor_addr));

            // This node is addressed to us.  We need to pick up this ABF.
            if (neighbor_addr == local_ip_addr)
            {
                // Validate that we have this group, otherwise just move on.
                if (FindMulticastGroup(grp_addr) == NULL)
                {
                    printf("        Multicast group not found; skipping.\n");
                    continue;
                }

                char hexABF[BLOOM_FILTER_SIZE * 2 + 1];
                bloom_filter_to_hex_string(currentNeighbor->ABF, hexABF, BLOOM_FILTER_SIZE);
                printf("        Inserting m %s and n %s with ABF %s.\n", 
                                inet_ntoa(grp_addr), inet_ntoa(srcAddr), hexABF);
                InsertMulticastNeighbor(grp_addr, srcAddr, currentNeighbor->ABF); 
            }
        }
    }
}
